﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace CalendarControl
{
    public partial class Calendar : UserControl
    {
        private const string CALENDAR_BACKCOLOR = "AliceBlue";
        private const string CALENDAR_HEADER_COLOR = "SteelBlue";
        private const string OTHER_HOLIDAY_COLOR = "LightSkyBlue";
        private const string WEEKLY_HOLIDAY_COLOR = "Pink";
        private Color _SpecialHolidaysColor;
        private bool _painted = false;
        private DateTime _SelectedDate = System.DateTime.Today;
        private bool _showSelectedDate;
        private string _SpecialHolidays = string.Empty;
        private DayOfWeek[] _weeklyHolidays;

        public Calendar()
        {
            InitializeComponent();
        }

        #region Click Event

        public delegate void ClickedHandler();

        [Category("Action")]
        public event ClickedHandler Clicked;

        protected virtual void OnClick()
        {
            if (Clicked != null)
            {
                Clicked();
            }
        }

        #endregion Click Event

        #region Properties

        [Browsable(true)]
        public Color SpecialHolidaysColor
        {
            get { return _SpecialHolidaysColor; }
            set
            {
                _SpecialHolidaysColor = value;
                Refresh();
            }
        }

        [Browsable(true)]
        public DateTime SelectedDate
        {
            get { return _SelectedDate; }
            set
            {
                _SelectedDate = value;
                Refresh();
                SetSelectedDate(SelectedDate);
            }
        }

        [Browsable(true), DefaultValue(false)]
        public bool ShowSelectedDate
        {
            get
            {
                return this._showSelectedDate;
            }
            set
            {
                this._showSelectedDate = value;
                Refresh();
                ResetLabelBorderStyle();
            }
        }

        public string SpecialHolidays
        {
            get { return _SpecialHolidays; }
            set
            {
                _SpecialHolidays = value;
                Refresh();
                if (_painted)
                {
                    MarkHolidays();
                }
            }
        }

        [Browsable(true)]
        public DayOfWeek[] WeeklyHolidays
        {
            get { return _weeklyHolidays; }
            set
            {
                _weeklyHolidays = value;
                this.Refresh();
            }
        }

        #endregion Properties

        #region Methods

        private void HideEndLabels()
        {
            foreach (Control control in pnlDates.Controls)
            {
                if (control is Label)
                {
                    int tempInt = 0;
                    if (!int.TryParse(control.Text.Trim(), out tempInt))
                    {
                        control.Visible = false;
                    }
                }
            }
        }

        private void HideStartLabels(int count)
        {
            for (int i = 1; i < count + 1; i++)
            {
                Control[] controls = pnlDates.Controls.Find("lblday" + i.ToString(), false);
                Label tempLabel = (Label)(controls[0]);
                tempLabel.Visible = false;
            }
        }

        private void InitializeControl()
        {
            foreach (Control control in pnlDates.Controls)
            {
                if (control is Label)
                {
                    control.Visible = true;
                    control.Text = string.Empty;
                    control.BackColor = Color.AliceBlue;
                }
            }
        }

        private void LoadCalendars()
        {
            PopulateCalendar(SelectedDate);

            MarkHolidays();

            if (_showSelectedDate)
            {
                SetSelectedDate(SelectedDate);
            }
        }

        private void MarkHolidays()
        {
            if (!string.IsNullOrEmpty(SpecialHolidays))
            {
                string[] arrHolidays = SpecialHolidays.Split(',');

                foreach (Control control in pnlDates.Controls)
                {
                    if (control is Label)
                    {
                        if (control.Visible)
                        {
                            control.BackColor = Color.FromName(CALENDAR_BACKCOLOR);

                            int date = Convert.ToInt32(control.Text.Trim());

                            if (arrHolidays.Contains(date.ToString()))
                            {
                                control.BackColor = _SpecialHolidaysColor;
                            }

                            DateTime curDate = new DateTime(SelectedDate.Year, SelectedDate.Month, date);

                            DayOfWeek dayOfWeek = curDate.DayOfWeek;
                        }
                    }
                }
            }
        }

        private void PopulateCalendar(DateTime selectedDate)
        {
            InitializeControl();

            int noOfDaysInMonth = DateTime.DaysInMonth(selectedDate.Year, selectedDate.Month);

            DateTime startDate = HelperMethods.GetStartOfMonth(selectedDate.Month, selectedDate.Year);
            DateTime endDate = HelperMethods.GetEndOfMonth(selectedDate.Month, selectedDate.Year);

            DayOfWeek startDayOfWeek = startDate.DayOfWeek;
            DayOfWeek endDayOfWeek = endDate.DayOfWeek;

            switch (startDayOfWeek)
            {
                case DayOfWeek.Friday:
                    HideStartLabels(4);
                    SetDateLabels(noOfDaysInMonth, 4);
                    break;

                case DayOfWeek.Monday:
                    SetDateLabels(noOfDaysInMonth, 0);
                    break;

                case DayOfWeek.Saturday:
                    HideStartLabels(5);
                    SetDateLabels(noOfDaysInMonth, 5);
                    break;

                case DayOfWeek.Sunday:
                    HideStartLabels(6);
                    SetDateLabels(noOfDaysInMonth, 6);
                    break;

                case DayOfWeek.Thursday:
                    HideStartLabels(3);
                    SetDateLabels(noOfDaysInMonth, 3);
                    break;

                case DayOfWeek.Tuesday:
                    HideStartLabels(1);
                    SetDateLabels(noOfDaysInMonth, 1);
                    break;

                case DayOfWeek.Wednesday:
                    HideStartLabels(2);
                    SetDateLabels(noOfDaysInMonth, 2);
                    break;
            }

            HideEndLabels();

            lblMonth.Text = HelperMethods.LoadMonthName((Months)selectedDate.Month) + ", " + SelectedDate.Year;
        }

        private void ResetLabelBorderStyle()
        {
            foreach (Control control in pnlDates.Controls)
            {
                if (control is Label)
                {
                    Label tempLbl = (Label)control;

                    if (tempLbl.BorderStyle == BorderStyle.FixedSingle)
                    {
                        tempLbl.BorderStyle = BorderStyle.None;
                    }
                }
            }
        }

        private void SetDateLabels(int dayCount, int count)
        {
            int pending = dayCount;

            for (int i = 1; i < dayCount + count + 1; i++)
            {
                Control[] controls = pnlDates.Controls.Find("lblday" + i.ToString(), false);
                Label tempLabel = (Label)(controls[0]);
                if (tempLabel.Visible)
                {
                    if (pending > 0)
                    {
                        tempLabel.Text = (dayCount - pending + 1).ToString();
                        pending--;
                    }
                }
            }
        }

        private void SetSelectedDate(DateTime selectedDate)
        {
            int day = selectedDate.Day;

            foreach (Control control in pnlDates.Controls)
            {
                if (control is Label)
                {
                    Label tempLabel = (Label)control;

                    if (tempLabel.Visible)
                    {
                        if (tempLabel.Text == day.ToString())
                        {
                            tempLabel.BorderStyle = BorderStyle.FixedSingle;
                        }
                        else
                        {
                            tempLabel.BorderStyle = BorderStyle.None;
                        }
                    }
                }
            }
        }

        #endregion Methods

        #region Events

        protected override void OnCreateControl()
        {
            base.OnCreateControl();

            InitializeControl();

            this.Width = 175;
            this.Height = 180;
            this.Font = new Font("Segoe UI", 8, FontStyle.Regular, GraphicsUnit.Point, 0);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            LoadCalendars();

            _painted = true;
        }

        #endregion Events

        private void lblNext_Click(object sender, EventArgs e)
        {
            DateTime tempDate = SelectedDate.AddMonths(1);
            DateTime nextMonth = new DateTime(tempDate.Year, tempDate.Month, 1);

            PopulateCalendar(nextMonth);

            SelectedDate = nextMonth;
        }

        private void lblPrevious_Click(object sender, EventArgs e)
        {
            DateTime tempDate = SelectedDate.AddMonths(-1);
            DateTime previousMonth = new DateTime(tempDate.Year, tempDate.Month, 1);

            PopulateCalendar(previousMonth);

            SelectedDate = previousMonth;
        }
    }
}